﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.Windows.Media.Media3D10
{
    /// <summary>
    /// Permet l'assignation des matrices les plus communes dans l'effet associé
    /// </summary>
    public sealed class CommonMatrixEffectParameter : EffectParameter
    {
        #region Dependency Properties
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="ApplyWorldMatrix"/>.
        /// </summary>
        public static readonly DependencyProperty ApplyWorldMatrixProperty =
            DependencyProperty.Register("ApplyWorldMatrix", typeof(bool), typeof(CommonMatrixEffectParameter));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="ApplyViewMatrix"/>.
        /// </summary>
        public static readonly DependencyProperty ApplyViewMatrixProperty =
            DependencyProperty.Register("ApplyViewMatrix", typeof(bool), typeof(CommonMatrixEffectParameter));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="ApplyProjectionMatrix"/>.
        /// </summary>
        public static readonly DependencyProperty ApplyProjectionMatrixProperty =
            DependencyProperty.Register("ApplyProjectionMatrix", typeof(bool), typeof(CommonMatrixEffectParameter));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="ApplyWorldViewMatrix"/>.
        /// </summary>
        public static readonly DependencyProperty ApplyWorldViewMatrixProperty =
            DependencyProperty.Register("ApplyWorldViewMatrix", typeof(bool), typeof(CommonMatrixEffectParameter));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="ApplyViewProjectionMatrix"/>.
        /// </summary>
        public static readonly DependencyProperty ApplyViewProjectionMatrixProperty =
            DependencyProperty.Register("ApplyViewProjectionMatrix", typeof(bool), typeof(CommonMatrixEffectParameter));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="ApplyWorldViewProjectionMatrix"/>.
        /// </summary>
        public static readonly DependencyProperty ApplyWorldViewProjectionMatrixProperty =
            DependencyProperty.Register("ApplyWorldViewProjectionMatrix", typeof(bool), typeof(CommonMatrixEffectParameter));
        #endregion

        #region Data
        private const string g_mWorld = "g_mWorld";
        private const string g_mView = "g_mView";
        private const string g_mProjection = "g_mProjection";
        private const string g_mWorldView = "g_mWorldView";
        private const string g_mViewProjection = "g_mViewProjection";
        private const string g_mWorldViewProjection = "g_mWorldViewProjection";

        private SlimDX.Direct3D10.EffectMatrixVariable _world;
        private SlimDX.Direct3D10.EffectMatrixVariable _view;
        private SlimDX.Direct3D10.EffectMatrixVariable _projection;
        private SlimDX.Direct3D10.EffectMatrixVariable _worldView;
        private SlimDX.Direct3D10.EffectMatrixVariable _viewProjection;
        private SlimDX.Direct3D10.EffectMatrixVariable _worldViewProjection;
        #endregion

        #region Public Properties
        /// <summary>
        /// Indique si la matrice World doit être appliquée
        /// </summary>
        public bool ApplyWorldMatrix
        {
            get { return (bool)GetValue(ApplyWorldMatrixProperty); }
            set { SetValue(ApplyWorldMatrixProperty, value); }
        }

        /// <summary>
        /// Indique si la matrice View doit être appliquée
        /// </summary>
        public bool ApplyViewMatrix
        {
            get { return (bool)GetValue(ApplyViewMatrixProperty); }
            set { SetValue(ApplyViewMatrixProperty, value); }
        }

        /// <summary>
        /// Indique si la matrice Projection doit être appliquée
        /// </summary>
        public bool ApplyProjectionMatrix
        {
            get { return (bool)GetValue(ApplyProjectionMatrixProperty); }
            set { SetValue(ApplyProjectionMatrixProperty, value); }
        }

        /// <summary>
        /// Indique si la matrice WorldView doit être appliquée
        /// </summary>
        public bool ApplyWorldViewMatrix
        {
            get { return (bool)GetValue(ApplyWorldViewMatrixProperty); }
            set { SetValue(ApplyWorldViewMatrixProperty, value); }
        }

        /// <summary>
        /// Indique si la matrice ViewProjection doit être appliquée
        /// </summary>
        public bool ApplyViewProjectionMatrix
        {
            get { return (bool)GetValue(ApplyViewProjectionMatrixProperty); }
            set { SetValue(ApplyViewProjectionMatrixProperty, value); }
        }

        /// <summary>
        /// Indique si la matrice WorldViewProjection doit être appliquée
        /// </summary>
        public bool ApplyWorldViewProjectionMatrix
        {
            get { return (bool)GetValue(ApplyWorldViewProjectionMatrixProperty); }
            set { SetValue(ApplyWorldViewProjectionMatrixProperty, value); }
        }
        #endregion

        #region EffectParameter Overrides
        /// <summary>
        /// Appelé lors de l'initialisation de l'effet associé
        /// </summary>
        /// <param name="effect">Effet associé</param>
        protected override void OnInitEffect(SlimDX.Direct3D10.Effect effect)
        {
            base.OnInitEffect(effect);
            if (effect != null)
            {
                _world = effect.GetVariableByName("g_mWorld").AsMatrix();
                _view = effect.GetVariableByName("g_mView").AsMatrix();
                _projection = effect.GetVariableByName("g_mProjection").AsMatrix();
                _worldView = effect.GetVariableByName("g_mWorldView").AsMatrix();
                _viewProjection = effect.GetVariableByName("g_mViewProjection").AsMatrix();
                _worldViewProjection = effect.GetVariableByName("g_mWorldViewProjection").AsMatrix();
            }
            else
            {
                _world = null;
                _view = null;
                _projection = null;
                _worldView = null;
                _viewProjection = null;
                _worldViewProjection = null;
            }
        }

        /// <summary>
        /// En cas de substitution dans une classe dérivée, participe au dessin des objets dans le pipeline DirectX10
        /// </summary>
        /// <param name="context">Contexte de dessin.</param>
        /// <exception cref="ArgumentNullException">Soulevée si le contexte est null.</exception>
        protected override void OnDraw(DrawingContext context)
        {
            if (object.ReferenceEquals(context, null))
                throw new ArgumentNullException("context");

            if (Effect == null) return;

            if (this.ApplyWorldMatrix)
            {
                _world.SetMatrix(context.WorldMatrix);
            }
            if (this.ApplyViewMatrix)
            {
                _view.SetMatrix(context.ViewMatrix);
            }
            if (this.ApplyProjectionMatrix)
            {
                _projection.SetMatrix(context.ProjectionMatrix);
            }
            if (this.ApplyWorldViewMatrix)
            {
                SlimDX.Matrix wvMatrix = context.WorldMatrix * context.ViewMatrix;
                _worldView.SetMatrix(wvMatrix);
            }
            if (this.ApplyViewProjectionMatrix)
            {
                SlimDX.Matrix vpMatrix = context.ViewMatrix * context.ProjectionMatrix;
                _viewProjection.SetMatrix(vpMatrix);
            }
            if (this.ApplyWorldViewProjectionMatrix)
            {
                SlimDX.Matrix wvpMatrix = context.WorldMatrix * context.ViewMatrix * context.ProjectionMatrix;
                _worldViewProjection.SetMatrix(wvpMatrix);
            }
        }
        #endregion

        #region Freezable Implementation
        /// <summary>
        /// Une fois implémentée dans une classe dérivée, initialise une nouvelle instance de la classe dérivée <see cref="Freezable"/>. 
        /// </summary>
        /// <returns>Nouvelle instance.</returns>
        protected override Freezable CreateInstanceCore()
        {
            return new CommonMatrixEffectParameter();
        }
        #endregion
    }
}
